﻿using HY.DataCore;
using HY.Entity;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace HY.DataAchieve.EntityFramework
{
    /// <summary>
    /// EF仓储基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class EFRepository<T> : IEFRepository<T> where T : class
    {
        private EntityContext dbContext = null;
        IDbSet<T> DbSet;

        public EFRepository(EntityContext _dbContext)
        {
            dbContext = _dbContext;
            DbSet = dbContext.Set<T>();
        }

        public IQueryable<T> Table => DbSet;

        /// <summary>
        /// 主键查询
        /// </summary>
        /// <param name="keyValues">键值</param>
        /// <returns>实体</returns>
        public virtual T Find(params object[] keyValues)
        {
            return this.DbSet.Find(keyValues);
        }

        /// <summary>
        /// 添加实体
        /// </summary>
        /// <param name="entity">实体</param>
        public void Add(T entity)
        {
            if (entity == null)
            { return; }

            this.DbSet.Add(entity);
        }

        /// <summary>
        /// 批量添加实体
        /// </summary>
        /// <param name="entities">实体集合</param>
        public void AddRange(IEnumerable<T> entities)
        {
            if (entities == null || entities.Count() == 0)
            { return; }

            //System.Data.Entity.DbSet<TEntity> set = this.DbSet;
            var set = this.DbSet;
            bool autoDetectChangesEnabled = this.dbContext.Configuration.AutoDetectChangesEnabled;
            this.dbContext.Configuration.AutoDetectChangesEnabled = false;

            foreach (T entity in entities)
            {
                if (entity == null)
                { continue; }

                set.Add(entity);
            }

            this.dbContext.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
        }

        /// <summary>
        /// 更改实体
        /// </summary>
        /// <param name="entity">实体对象</param>
        public void Update(T entity)
        {
            if (entity == null)
            { return; }

            System.Data.Entity.Infrastructure.DbEntityEntry<T> entry = this.dbContext.Entry(entity);
            if (entry.State == System.Data.Entity.EntityState.Detached)
            {
                this.DbSet.Attach(entity);
            }
            entry.State = System.Data.Entity.EntityState.Modified;
        }

        /// <summary>
        /// 批量更改实体
        /// </summary>
        /// <param name="entities">实体集合</param>
        public void UpdateRange(IEnumerable<T> entities)
        {
            if (entities == null || entities.Count() == 0)
            { return; }

            var set = this.DbSet;
            bool autoDetectChangesEnabled = this.dbContext.Configuration.AutoDetectChangesEnabled;
            this.dbContext.Configuration.AutoDetectChangesEnabled = false;

            foreach (T entity in entities)
            {
                if (entity == null)
                { continue; }

                System.Data.Entity.Infrastructure.DbEntityEntry<T> entry = this.dbContext.Entry(entity);
                if (entry.State == System.Data.Entity.EntityState.Detached)
                {
                    set.Attach(entity);
                }
                entry.State = System.Data.Entity.EntityState.Modified;
            }

            this.dbContext.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
        }

        /// <summary>
        /// 主键删除实体
        /// </summary>
        /// <param name="key">键值</param>
        public void Delete(object key)
        {
            if (key == null || string.IsNullOrWhiteSpace(key.ToString()))
            { return; }

            T entity = this.Find(key);

            this.Delete(entity);
        }

        /// <summary>
        /// 删除实体
        /// </summary>
        /// <param name="entity">实体</param>
        public void Delete(T entity)
        {
            if (entity == null)
            { return; }

            //System.Data.Entity.DbSet<TEntity> set = this.DbSet;
            var set = this.DbSet;
            System.Data.Entity.Infrastructure.DbEntityEntry<T> entry = this.dbContext.Entry(entity);
            if (entry.State == System.Data.Entity.EntityState.Detached)
            {
                set.Attach(entity); set.Remove(entity);
            }
            else
            { entry.State = System.Data.Entity.EntityState.Deleted; }
        }

        /// <summary>
        /// 批量删除实体
        /// </summary>
        /// <param name="entities">实体集合</param>
        public void DeleteRange(IEnumerable<T> entities)
        {
            if (entities == null || entities.Count() == 0)
            { return; }
            var set = this.DbSet;

            bool autoDetectChangesEnabled = this.dbContext.Configuration.AutoDetectChangesEnabled;
            this.dbContext.Configuration.AutoDetectChangesEnabled = false;

            foreach (T entity in entities)
            {
                if (entity == null)
                { continue; }

                System.Data.Entity.Infrastructure.DbEntityEntry<T> entry = this.dbContext.Entry(entity);
                if (entry.State == System.Data.Entity.EntityState.Detached)
                {
                    set.Attach(entity);
                }
                else
                {
                    entry.State = System.Data.Entity.EntityState.Deleted;
                }
            }

            this.dbContext.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
        }

        /// <summary>
        /// 获取分页数据（单字段排序）
        /// </summary>
        /// <param name="condition">查询条件</param>
        /// <param name="pageIndex">当前页码</param>
        /// <param name="pageSize">每页大小</param>
        /// <param name="orderBy">排序条件</param>
        /// <param name="isAsc">是否升序</param>
        /// <returns>IQueryable 泛型集合</returns>
        public Tuple<int, IList<T>> GetPageList<Tkey>(Expression<Func<T, bool>> condition, int pageIndex, int pageSize, Expression<Func<T, Tkey>> orderBy, bool isAsc)
        {
            //条件过滤
            var query = this.DbSet.Where(condition);
            int total = query.Count();
            //倒序或升序
            if (isAsc)
                return new Tuple<int, IList<T>>(total, query.OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList());
            else
                return new Tuple<int, IList<T>>(total, query.OrderByDescending(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList());
        }

        /// <summary>
        /// 获取分页数据（多字段排序）
        /// </summary>
        /// <param name="condition">查询条件</param>
        /// <param name="pageIndex">页索引</param>
        /// <param name="pageSize">页大小</param>
        /// <param name="orderByExpression">排序条件</param>
        /// <returns></returns>
        public Tuple<int, IList<T>> GetPageList(Expression<Func<T, bool>> condition, int pageIndex, int pageSize, params OrderByExpression[] orderByExpression)
        {
            //条件过滤
            var query = this.DbSet.Where(condition);
            //创建表达式变量参数
            var parameter = Expression.Parameter(typeof(T), "o");
            if (orderByExpression != null && orderByExpression.Length > 0)
            {
                for (int i = 0; i < orderByExpression.Length; i++)
                {
                    //根据属性名获取属性
                    var property = typeof(T).GetProperty(orderByExpression[i].PropertyName);
                    //创建一个访问属性的表达式
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);
                    string OrderName = "";
                    if (i > 0)
                    {
                        OrderName = orderByExpression[i].IsDesc ? "ThenByDescending" : "ThenBy";
                    }
                    else
                        OrderName = orderByExpression[i].IsDesc ? "OrderByDescending" : "OrderBy";
                    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), OrderName, new Type[] { typeof(T), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));

                    query = query.Provider.CreateQuery<T>(resultExp);
                }
            }
            int total = query.Count();
            return new Tuple<int, IList<T>>(total, query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList());
        }
    }
}
